/** This is main driver program of the query processor **/

import java.io.*;
import java.util.Vector;

import qp.utils.*;
import qp.operators.*;
import qp.optimizer.*;
import qp.parser.*;

public class QueryMain
{

	static PrintWriter out;
	static int numAtts;

	public static void main(String[] args) throws ClassNotFoundException 
	{

		if (args.length != 2)
		{
			System.out
					.println("usage: java QueryMain <queryfilename> <resultfile>");
			System.exit(1);
		}

		/** Enter the number of bytes per page **/

		System.out.println("enter the number of bytes per page");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String temp;
		try
		{
			temp = in.readLine();
			int pagesize = Integer.parseInt(temp);
			Batch.setPageSize(pagesize);
		} catch (Exception e)
		{
			e.printStackTrace();
		}

		String queryfile = args[0];
		String resultfile = args[1];
		FileInputStream source = null;
		try
		{
			source = new FileInputStream(queryfile);
		} catch (FileNotFoundException ff)
		{
			System.out.println("File not found: " + queryfile);
			System.exit(1);
		}

		/** scan the query **/

		Scaner sc = new Scaner(source);
		parser p = new parser();
		p.setScanner(sc);

		/** parse the query **/

		try
		{
			p.parse();
		} catch (Exception e)
		{
			System.out.println("Exception occured while parsing");
			System.exit(1);
		}

		/** SQLQuery is the result of the parsing **/

		SQLQuery sqlquery = p.getSQLQuery();
		int numJoin = sqlquery.getNumJoin();
		
		if(sqlquery.getJoinList().size() == 0 && sqlquery.getFromList().size() > 1)
		{
			System.out.println("do not support cross product");
			System.exit(1);
		}

		/**
		 * If there are joins then assigns buffers to each join operator while
		 * preparing the plan
		 **/
		/**
		 * As buffer manager is not implemented, just input the number of
		 * buffers available
		 **/

		if (numJoin != 0)
		{
			System.out.println("enter the number of buffers available");

			try
			{
				temp = in.readLine();
				int numBuff = Integer.parseInt(temp);
				BufferManager bm = new BufferManager(numBuff, numJoin);
			} catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		else
		{
			BufferManager bm = new BufferManager(6, 0);//added by ZJX
		}
		/** Let check the number of buffers available is enough or not **/

		int numBuff = BufferManager.getBuffersPerJoin();
		if (numJoin > 0 && numBuff < 3)
		{
			System.out
					.println("Minimum 3 buffers are required per a join operator ");
			System.exit(1);
		}

		/**
		 * This part is used When some random initial plan is required instead
		 * of comple optimized plan
		 **/
		/**
		 * RandomInitialPlan rip = new RandomInitialPlan(sqlquery); Operator
		 * logicalroot = rip.prepareInitialPlan(); PlanCost pc = new PlanCost();
		 * int initCost = pc.getCost(logicalroot); Debug.PPrint(logicalroot);
		 * System.out.print("   "+initCost); System.out.println();
		 **/

		/**
		 * Use random Optimization algorithm to get a random optimized execution
		 * plan
		 **/
		RandomOptimizer ro = new RandomOptimizer(sqlquery);
		Operator logicalroot = ro.getOptimizedPlan_SA();
		//Operator logicalroot = ro.getOptimizedPlan_II();
		if (logicalroot == null)
		{
			System.out.println("root is null");
			System.exit(1);
		}

		/** preparing the execution plan **/
       
		Operator root = RandomOptimizer.makeExecPlan(logicalroot);

		/** Print final Plan **/
		System.out
				.println("----------------------Execution Plan----------------");
		Debug.PPrint(root);
		System.out.println();

		/** Ask user whether to continue execution of the program **/

		System.out.println("enter 1 to continue, 0 to abort ");

		try
		{
			temp = in.readLine();
			int flag = Integer.parseInt(temp);
			if (flag == 0)
			{
				System.exit(1);
			}

		} catch (Exception e)
		{
			e.printStackTrace();
		}

		long starttime = System.currentTimeMillis();

		if (root.open() == false)
		{
			System.out.println("Root: Error in opening of root");
			System.exit(1);
		}
		try
		{
			out = new PrintWriter(
					new BufferedWriter(new FileWriter(resultfile)));
		} catch (IOException io)
		{
			System.out.println("QueryMain:error in opening result file: "
					+ resultfile);
			System.exit(1);
		}

		/** print the schema of the result **/
		Schema schema = root.getSchema();
		numAtts = schema.getNumCols();
		printSchema(schema);
		Batch resultbatch=null;

		/** print each tuple in the result **/
		
        //****************************************************modified by ZJX, realizing orderby and distinct eliminating
	/*if(sqlquery.isDistinct()||sqlquery.isAscent()||sqlquery.isDescendent())
	{
		String unSortedFile="OrderByTemp.tem";
		try {
			FileOutputStream fos=new FileOutputStream(unSortedFile);
			ObjectOutputStream oos=new ObjectOutputStream(fos);
			Batch tempOutputBatch;
			
			while((tempOutputBatch=root.next())!=null) {
				int tempSize=tempOutputBatch.size();
				oos.writeObject(tempOutputBatch);
			}
			fos.close();
			oos.close();
		}
		catch (Exception e) {    	
		}
		
		
		
		   String sortedFile="";
		   Vector<Attribute> attrVector=new Vector<Attribute>();//convert attr to Vector
		   if(sqlquery.orderby!=null)
		   {
		     Attribute attr=sqlquery.getOrderby();
		     attrVector.add(attr);
		   }
		   else 
		   {
			 Attribute attr=root.getSchema().getAttribute(0);
		     attrVector.add(attr);
		   }
		   if(sqlquery.isDescendent())
		   {
			   SortDescendent esd=new SortDescendent(unSortedFile,attrVector,root.getSchema());
		       try {
			       sortedFile=esd.MergeSort();			
		           }
		            catch (Exception e) {    		
		           }	
		   }
		   else
		   {
		       ExternalMergeSort es=new ExternalMergeSort(unSortedFile,attrVector,root.getSchema());
		       try {
			       sortedFile=es.MergeSort();			
		           }
		            catch (Exception e) {    		
		           }	
	
		   }
	        
	       try
	       { 
	    	   FileInputStream fileIn=new FileInputStream(sortedFile);
		       ObjectInputStream oi=new ObjectInputStream(fileIn);
		       boolean IsFirst=true;
		       Tuple oldTuple=null;
		       boolean restContent=true;
		       Vector<Tuple> oldTuples =new Vector<Tuple>();
	           while (restContent)
	  		   {
	        	   try
	        	   {
	        	    resultbatch =(Batch)oi.readObject();
	        	   }
	        	   catch(IOException e){ 
	        		   restContent=false;
	        		   break;
	        	    }
	        	   
	        	   if(IsFirst&&sqlquery.isDistinct())
	        	   {
	  				   oldTuple=resultbatch.elementAt(0);
	  				   IsFirst=false;
	  				   oldTuples.add(oldTuple);//initialize the vector
	  				   printTuple(resultbatch.elementAt(0));
	        	   }
	  			   for (int i = 0; i < resultbatch.size(); i++)
	  			    {
	  				   if(sqlquery.isDistinct())
	  				   {
	  					   int OriginalSize=oldTuples.size();
	  					   for(int j=0;j<OriginalSize;j++)
	  					   {
	  						   int compare=Tuple.AreTuplesEqual(resultbatch.elementAt(i),oldTuples.elementAt(j),numAtts);
	  						   if(compare==0)//equal
	  						   {
	  							   break;//skip out of the for loop
	  						   }
	  						   else if(compare==-1)//partly equal
	  						   {
	  							   if(j==(OriginalSize-1))
	  							   {
	  								   oldTuples.add(resultbatch.elementAt(i));
	  								   printTuple(resultbatch.elementAt(i));
	  							   }
	  						   }
	  						   else//compare==1,not equal
	  						   {
	  							   oldTuples.remove(j);
	  							   j--;
	  							   OriginalSize--;
	  							   
	  							   if(j==(OriginalSize-1))
	  							   {
	  								   oldTuples.add(resultbatch.elementAt(i));
	  								   printTuple(resultbatch.elementAt(i));
	  							   }
	  							   
	  						   }
	  						   
	  					   }
	  	
	  				   }
	  				   if(!sqlquery.isDistinct())
	  				   {
	  					  printTuple(resultbatch.elementAt(i));
	  				   }
	  			    }
	  		   }
	  		   root.close();
	  		   out.close();
	       }
	       catch (IOException e)
		   {
			// TODO Auto-generated catch block
			e.printStackTrace();
		   }
	}
	else
	{
		while ((resultbatch = root.next()) != null)
		{
			for (int i = 0; i < resultbatch.size(); i++)
			{
				printTuple(resultbatch.elementAt(i));
			}
		}
		root.close();
		out.close();
	}*/
		
		//**************************modified end

		while((resultbatch=root.next())!=null){
		    for(int i=0;i<resultbatch.size();i++){
			printTuple(resultbatch.elementAt(i));
		    }
		}
		
		root.close();
		out.close();
	
		long endtime = System.currentTimeMillis();
		double executiontime = (endtime - starttime) / 1000.0;
		System.out.println("Execution time = " + executiontime);

	}

	protected static void printTuple(Tuple t)
	{
		for (int i = 0; i < numAtts; i++)
		{
			Object data = t.dataAt(i);
			if (data instanceof Integer)
			{
				out.print(((Integer) data).intValue() + "\t");
			} else if (data instanceof Float)
			{
				out.print(((Float) data).floatValue() + "\t");
			} else
			{
				out.print(((String) data) + "\t");
			}
		}
		out.println();
	}

	protected static void printSchema(Schema schema)
	{
		for (int i = 0; i < numAtts; i++)
		{
			Attribute attr = schema.getAttribute(i);
			out.print(attr.getTabName() + "." + attr.getColName() + "  ");
		}
		out.println();
	}

}
